process.env.TZ = 'Asia/Shanghai';

fs = require('fs');
// mysql = require('mysql');

https = require('https');

var uim = require('./uim.js');

constains = require('constants');

var exec_request = function (req, res) {
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end('{"status":"ERROR"}');
};

// 环境配置（以下划线开头的配置表示允许在运行中重新赋值并立即生效）
const ENV = {
    SOCKET_ROUTER_ID: fs.readFileSync('./.env/SOCKET_ROUTER_ID', 'utf-8').trim(), // 路由服务器编号
    LISTEN_PORT: fs.readFileSync('./.env/LISTEN_PORT', 'utf-8').trim(),
    REDIS: {
        PORT: fs.readFileSync('./.env/redis.port', 'utf-8').trim(),
        HOST: fs.readFileSync('./.env/redis.host', 'utf-8').trim(),
        PASSWORD: fs.readFileSync('./.env/redis.password', 'utf-8').trim(),
    },
    HTTPS: {
        key: fs.readFileSync('socket-router.key'),
        cert: fs.readFileSync('socket-router.pem'),
        // secureOptions: constains.SSL_OP_NO_TLSv1_2 | constains.SSL_OP_NO_TLSv1_1,
    },
    _STATE: 0, // 状态：0启动中，1就绪，2活动，3暂停
    _DEBUG_MODE: true,
    _ACTIVE_TIME_LIMIT: 10000, // 超过10秒没有回应的服务器不再分配新的连接
    // _IDC_LINE: 1, // 机房线路
}

console.log(ENV);

var default_socket_server_obj = {
    // id: 0,
    // host: 'socketserver-001.yourdomain.cn:5201', // wss/https地址
    clients: 0, // 当前在线客户数
    linking: 0, // 截止clients变量更新前，已经转发的客户数
    last_active_time: 0, // 最后活动时间（毫秒时间戳）
    last_report_time: 0, // 最后一次上报时间（毫秒时间戳）
    enable: false, // 是否可用
}
// 检查辖下服务器状态变化
var report_socket_server_state = function () {
    var now_time = new Date().getTime();
    for (var tmp_i in socket_server_list) {
        if (socket_server_list[tmp_i].last_active_time > socket_server_list[tmp_i].last_report_time
            && now_time - socket_server_list[tmp_i].last_active_time > ENV._ACTIVE_TIME_LIMIT
        ) {
            socket_server_list[tmp_i].enable = false;
            socket_server_list[tmp_i].last_report_time = now_time;
            // todo 向控制台发出socket-server掉线信息
            console.log('socket-server掉线：');
            console.log(socket_server_list[tmp_i]);
        }
    }
    setTimeout(report_socket_server_state, ENV._ACTIVE_TIME_LIMIT);
}
// 该路由辖下的服务器列表
var socket_server_list = {
    // '1': {}
};

// 该路由所服务的应用列表 
var app_list = {
    // '1': {
    //     app_id: 0,
    //     app_name: '',
    //     enable: true, // 应用启用状态
    // }
};


var init_app_list = function () {
    console_redis_pub.get('console_deploy|app_list|socket_router_' + ENV.SOCKET_ROUTER_ID, function (err, result) {
        if (err) {
            return console.log('读取应用列表失败');
        }
        var tmp_app_list = uim.parseJSON(result);
        if (!tmp_app_list) {
            return console.log('解析应用列表时数据异常');
        }
        for (var tmp_i in tmp_app_list) {
            app_list[tmp_app_list[tmp_i].app_id] = tmp_app_list[tmp_i];
        }
        console.log('初始应用列表成功：');
        console.log(app_list);
        init_socket_server_list(); //初始辖下socket-server列表
    });
}

var init_socket_server_list = function () {
    // 从redis/mysql中读取列表
    // 遍历并初始redis列表
    console_redis_pub.get('console_deploy|socket_server_list|socket_router_' + ENV.SOCKET_ROUTER_ID, function (err, result) {
        if (err) {
            throw '读取socket_server列表失败';
        }
        var tmp_socket_server_list = uim.parseJSON(result || '[]');
        if (!tmp_socket_server_list) {
            throw '解析socket_server列表时数据异常';
        }
        for (var tmp_i in tmp_socket_server_list) {
            socket_server_list[tmp_socket_server_list[tmp_i].socket_server_id] = Object.assign(tmp_socket_server_list[tmp_i], default_socket_server_obj);
        }
        console.log('初始socket-server列表成功：');
        console.log(socket_server_list);
        init_redis_subscribe();
    });
}

var redis = require('redis');

// 用于连接router本机redis，用于与socket-server保持通信，接收update-server
var console_redis_sub = undefined;

// 用于连接router本机redis，用于与socket-server保持通信，向update-server发出广播事件
var console_redis_pub = redis.createClient(ENV.REDIS.PORT, ENV.REDIS.HOST, { auth_pass: ENV.REDIS.PASSWORD });
console_redis_pub.on('ready', function (err) {
    // 如果连接控制台失败
    if (err) {
        throw 'pub连接本地控制台失败'
    } else {
        console.log('pub已连接到本地控制台');
        init_app_list(); // 初始应用列表
    }
});
var init_redis_subscribe = function () {
    console_redis_sub = redis.createClient(ENV.REDIS.PORT, ENV.REDIS.HOST, { auth_pass: ENV.REDIS.PASSWORD });
    console_redis_sub.on('ready', function (err) {
        // 如果连接控制台失败
        if (err) {
            throw 'sub连接本地控制台失败'
        } else {
            console.log('sub已连接到本地控制台');
        }
    });
    console_redis_sub.subscribe('update_socket_server'); // 订阅本router下属server的状态更新信息
    console_redis_sub.subscribe('emit_event'); // 如果是由router连接外部，则按router编号订阅广播事件，如果是从外部连接router，则只从本机订阅
    console_redis_sub.on('subscribe', function (channel, count) {
        console.log(`got subscribe event: ${channel} and count is ${count}`);
    });
    console_redis_sub.on('connect', function () {
        //订阅客户端接收到消息
        console_redis_sub.on('message', function (channel, message) {
            var time = new Date().getTime();
            var response = `${time} received message from ${channel}:${message}`;
            // console.log(response);
            switch (channel) {
                case 'update_socket_server':
                    update_socket_server(message, time);
                    break;
                case 'emit_event':
                    emit_event(message, time);
                case 'debug':
                    run_debug(message);
                default:
                    break;
            }
        });
        ENV._STATE = 1; // socket-router已就绪
        console.log('socket-router已就绪');
        report_socket_server_state();
    });
}

var update_socket_server = function (message, time) {
    var tmp_arr = message.split(',');
    if (tmp_arr.length != 2 || !socket_server_list[tmp_arr[0]]) {
        return;
    }
    socket_server_list[tmp_arr[0]].clients = tmp_arr[1] * 1;
    socket_server_list[tmp_arr[0]].linking = 0;
    socket_server_list[tmp_arr[0]].last_active_time = time;
    if (false === socket_server_list[tmp_arr[0]].enable) {
        socket_server_list[tmp_arr[0]].enable = true;
        // todo 向控制台发出socket-server上线信息
        console.log('socket-server上线：');
        console.log(socket_server_list[tmp_arr[0]]);
    }
}
var emit_event = function (message, time) {
    var tmp_emit_event = uim.parseJSON(message);
    // ['socket_server_id', 'account_id', 'message', 'need_back']
    var tmp_socket_server_id = tmp_emit_event.shift();
    if (!tmp_socket_server_id || !tmp_emit_event[0]) {
        return;
    }
    if (socket_server_list[tmp_socket_server_id].enable) {
        console_redis_pub.publish('socket_server_' + tmp_socket_server_id, tmp_emit_event, function (err, reply) {
            if (tmp_emit_event[2] && err) {
                // todo back and retry
            }
        });

    }
}

// 负载均衡可直接在router域名解析上做，每个router只负责一个机房内的路由

var get_enable_socket_server = function () {
    var tmp_i, tmp_socket_server_id, tmp_clients = online_client_max;
    var tmp_all;
    // 从服务器列表找出当前在线数最少的机器
    for (tmp_i in socket_server_list) {
        if (socket_server_list[tmp_i].enable) {
            tmp_all = socket_server_list[tmp_i].clients + socket_server_list[tmp_i].linking;
            if (tmp_all < tmp_clients) {
                tmp_socket_server_id = tmp_i;
                tmp_clients = tmp_all;
            }
        }
    }
    // if (tmp_clients > online_client_warn) uim.send_warn({
    //     title: '最少在线人数的服务器所维护的连接数超过预警值',
    //     message: { online_client_warn, socket_server_list }
    // });
    if (tmp_socket_server_id) {
        socket_server_list[tmp_socket_server_id].linking++;
    }
    return tmp_socket_server_id;
};

var online_client_number = 0; //在线总人数
var online_client_max = 9999; // 在线用户量大于此值的服务器不再分配请求
var online_client_warn = 8000; // 最少在线人数的服务器所维护的连接数超过此值则发送警告

// 用于判断单位时间内创建的客户端标识数量
var created_client_ids = [
    // {
    //     create_time: new Date().getTime(),
    //     client_id: ''
    // }
    // [new Date().getTime(), '']
];
var get_created_count = function (arr, time_index, after_time, left_index = 0, right_index = 0) {
    right_index = right_index || arr.length;
    if (arr.length == 0) {
        return 0;
    }
    if ((time_index === false ? arr[left_index] : arr[left_index][time_index]) > after_time) {
        if (arr[left_index - 1] && (time_index === false ? arr[left_index - 1] : arr[left_index - 1][time_index]) > after_time) {
            return get_created_count(arr, time_index, after_time, left_index);
        } else {
            return arr.length - left_index;
        }
    } else {

    }
}
// 临时存放新客户端序号 todo 启动时从redis中读取，使用后再通过redis队列批量写入数据库
var new_client_ids = [
    [0, 0],
    [1n, 1000n],
    [1001n, 2000n]
]; // 一次只从redis取一条数据，数据只包含最小值和最大值，格式：1,10000
var get_next_client_id = function () {
    if (!new_client_ids[0]) {
        // todo 发出警告
        return 0;
    }
    if (new_client_ids[0][0] < new_client_ids[0][1]) {
        new_client_ids.shift();
        if (new_client_ids.length < 10) {
            // redis
        }
        return get_next_client_id();
    }
    return new_client_ids[0][0]++;
}

var app = https.createServer(ENV.HTTPS, exec_request).listen(ENV.LISTEN_PORT);
var io = require('socket.io').listen(app);
io.sockets.on('connection', function (socket) {
    online_client_number++;
    console.log(uim.dateTime() + ' - 客户端连接：' + socket.request.connection.remoteAddress + '。在线总人数：' + online_client_number + '。socket.id：' + socket.id);
    //初始化socket
    socket.on('init', function (app_info, call_back) {
        // 校验app_info.deploy_app_id
        var verify_deploy_app_id = uim.verifySafeString(app_info.deploy_app_id, uim.config.enum.safeStringType.DEPLOY_APP_ID);
        if (!verify_deploy_app_id) {
            return call_back({ error: '校验失败' });
        }
        if (app_list[verify_deploy_app_id.id] === undefined) {
            return call_back({ error: '不接受该应用' });
        }
        if (!app_list[verify_deploy_app_id.id].enable) {
            return call_back({ error: '该应用暂不提供服务' });
        }

        var return_obj = {
            error: '',
            socket_server_list
        };
        // 如果没有传入init_id
        if (!app_info.init_id) {
            // todo 判断短时间内是否生成过多client_id

            return_obj.init_id = uim.createSafeString({
                int_unique: get_next_client_id(),
                int_tag: uim.config.enum.safeStringType.CLIENT_ID
            });
        } else {
            // 校验app_info.init_id
            var verify_init_id = uim.verifySafeString(app_info.init_id, uim.config.enum.safeStringType.CLIENT_ID);
            if (!verify_init_id) {
                return call_back({ error: '校验失败：init_id' });
            }
            // todo 取client_id信息，判断并记录init_id时间戳变早的情况（用户手动操作，或恢复系统镜像）
            return_obj.init_id = uim.createSafeString({
                int_unique: verify_init_id.id,
                int_tag: uim.config.enum.safeStringType.CLIENT_ID
            });
        }
        // // 校验app_info.session_id
        // var verify_session_id = uim.verifySafeString(app_info.session_id, uim.config.enum.safeStringType.SESSION_ID);
        // if (!verify_session_id) {
        //     return call_back({ error: '校验失败' });
        // }

        var new_socket_server_id = get_enable_socket_server();
        if (new_socket_server_id) {
            // 使用socket.id作为token
            var token = uim.createSafeString({
                int_unique: new_socket_server_id * 1,
                int_tag: uim.config.enum.safeStringType.SOCKET_ROUTER_TOKEN
            });
            return_obj.new_host = socket_server_list[new_socket_server_id].host;
            return_obj.token = token;
        } else {
            return_obj.error = '在线人数太多，工程师正在增加新服务器，请稍后...';
        }
        return call_back(return_obj);
    });

    //调试
    socket.on('debug', run_debug);

    socket.on('disconnect', function (data) {
        online_client_number--;
        console.log(uim.dateTime() + ' - 客户端断开：' + socket.request.connection.remoteAddress + '。在线总人数：' + online_client_number + '。socket.id：' + socket.id);
    });

    // 触发客户端初始事件
    socket.emit('init');

});
var run_debug = function (debug_code) {
    console.log('debug: ' + debug_code);
    //此调试方法只在调试模式下使用
    if (ENV._DEBUG_MODE) eval(debug_code);
}

console.log('router server v2.0 is running: ' + uim.dateTime());
